1use crate::copp::clarabel_backend::{ConstraintsClarabel, ObjConsClarabel};
29use crate::copp::copp3::Copp3Result;
30use crate::copp::copp3::formulation::{Copp3Problem, get_weight_a_copp3, get_weight_a_topp3};
31use crate::copp::copp3::opt3::clarabel_constraints::{
32 clarabel_standard_capacity_topp3, clarabel_standard_constraint_topp3,
33};
34use crate::copp::{
35 ClarabelOptions, CoppObjective, clarabel_to_copp3_solution, validate_copp3_objectives,
36};
37use crate::diag::{
38 CoppError, DebugVerboser, SilentVerboser, SummaryVerboser, TraceVerboser, Verboser, Verbosity,
39 check_boundary_state_copp3_valid, check_s_interval_valid, format_duration_human,
40};
41use crate::robot::robot_core::{Robot, RobotBasic, RobotTorque};
42use clarabel::algebra::CscMatrix;
43use clarabel::solver::SupportedConeT::{NonnegativeConeT, SecondOrderConeT};
44use clarabel::solver::{DefaultSolution, DefaultSolver, IPSolver, SupportedConeT};
45use core::f64;
46use itertools::{Itertools, izip};
47use nalgebra::{DMatrix, DVectorView};
48
49pub fn copp3_socp<'a, M: RobotTorque>(
70 problem: &Copp3Problem<'a, M>,
71 options: &ClarabelOptions,
72) -> Result<Copp3Result, CoppError> {
73 let (result, solution) = copp3_socp_expert(problem, options)?;
74 result.ok_or_else(|| CoppError::ClarabelSolverStatus("copp3_socp".into(), solution.status))
75}
76
77pub fn copp3_socp_expert<'a, M: RobotTorque>(
101 problem: &Copp3Problem<'a, M>,
102 options: &ClarabelOptions,
103) -> Result<(Option<Copp3Result>, DefaultSolution<f64>), CoppError> {
104 match options.verbosity() {
105 Verbosity::Silent => copp3_socp_core(problem, (options, SilentVerboser)),
106 Verbosity::Summary => copp3_socp_core(problem, (options, SummaryVerboser::new())),
107 Verbosity::Debug => copp3_socp_core(problem, (options, DebugVerboser::new())),
108 Verbosity::Trace => copp3_socp_core(problem, (options, TraceVerboser::new())),
109 }
110}
111
112fn copp3_socp_core<'a, M: RobotTorque>(
124 problem: &Copp3Problem<'a, M>,
125 options_verboser: (&ClarabelOptions, impl Verboser),
126) -> Result<(Option<Copp3Result>, DefaultSolution<f64>), CoppError> {
127 let (options, mut verboser) = options_verboser;
128 let idx_s_start = problem.idx_s_start;
129 let a_boundary = problem.a_boundary;
130 let b_boundary = problem.b_boundary;
131 let num_stationary = problem.num_stationary;
132 if verboser.is_enabled(Verbosity::Summary) {
133 verboser.record_start_time();
134 }
135 if verboser.is_enabled(Verbosity::Trace) {
136 let settings = options.clarabel_settings();
137 crate::verbosity_log!(
138 crate::diag::Verbosity::Summary,
139 "copp3_socp: options snapshot -> allow(almost={}, max_iter={}, max_time={}, callback_term={}, insufficient_progress={}), tol_gap_rel={}, tol_feas={}, max_iter={}, verbose={}",
140 options.is_allow(clarabel::solver::SolverStatus::AlmostSolved),
141 options.is_allow(clarabel::solver::SolverStatus::MaxIterations),
142 options.is_allow(clarabel::solver::SolverStatus::MaxTime),
143 options.is_allow(clarabel::solver::SolverStatus::CallbackTerminated),
144 options.is_allow(clarabel::solver::SolverStatus::InsufficientProgress),
145 settings.tol_gap_rel,
146 settings.tol_feas,
147 settings.max_iter,
148 settings.verbose
149 );
150 }
151 check_boundary_state_copp3_valid(a_boundary, b_boundary)?;
153 let n = problem.a_linearization.len() - 1;
154 let idx_s_final = idx_s_start + n;
155 if verboser.is_enabled(Verbosity::Summary) {
156 crate::verbosity_log!(
157 crate::diag::Verbosity::Summary,
158 "\ncopp3_socp started: {} <= idx_s <= {}, objectives = {}, s_len = {}.",
159 idx_s_start,
160 idx_s_final,
161 problem.objectives.len(),
162 problem.a_linearization.len()
163 );
164 }
165 check_s_interval_valid("copp3_socp", idx_s_start, idx_s_final)?;
166 validate_copp3_objectives(
167 "copp3_socp",
168 problem.objectives,
169 problem.robot.dim(),
170 problem.a_linearization.len(),
171 )?;
172 let (cap_val_std, cap_b_std, cap_cone_std) =
179 clarabel_standard_capacity_topp3(&problem.robot.constraints, (idx_s_start, idx_s_final));
180 let (cap_val_obj, cap_b_obj, cap_cone_obj, n_vars) =
181 clarabel_objective_capacity_copp3(n, problem.objectives, problem.robot);
182 if verboser.is_enabled(Verbosity::Debug) {
183 crate::verbosity_log!(
184 crate::diag::Verbosity::Summary,
185 "copp3_socp: capacity estimate std(val={cap_val_std}, b={cap_b_std}, cone={cap_cone_std}), obj(val={cap_val_obj}, b={cap_b_obj}, cone={cap_cone_obj}), n_vars={n_vars}."
186 );
187 }
188 let mut row = Vec::<usize>::with_capacity(cap_val_std + cap_val_obj);
191 let mut col = Vec::<usize>::with_capacity(cap_val_std + cap_val_obj);
192 let mut val = Vec::<f64>::with_capacity(cap_val_std + cap_val_obj);
193 let mut b = Vec::<f64>::with_capacity(cap_b_std + cap_b_obj);
194 let mut cones = Vec::<SupportedConeT<f64>>::with_capacity(cap_cone_std + cap_cone_obj);
195 if verboser.is_enabled(Verbosity::Trace) {
196 crate::verbosity_log!(
197 crate::diag::Verbosity::Summary,
198 "copp3_socp: allocated capacities row/col/val/b/cones <= {}/{}/{}/{}/{}",
199 cap_val_std + cap_val_obj,
200 cap_val_std + cap_val_obj,
201 cap_val_std + cap_val_obj,
202 cap_b_std + cap_b_obj,
203 cap_cone_std + cap_cone_obj
204 );
205 }
206 let s = problem
208 .robot
209 .constraints
210 .s_vec(idx_s_start, idx_s_final + 1)?;
211 let row_before_std = row.len();
212 let col_before_std = col.len();
213 let val_before_std = val.len();
214 let b_before_std = b.len();
215 let cones_before_std = cones.len();
216 clarabel_standard_constraint_topp3(
217 &problem.as_topp3_problem(),
218 &s,
219 (&mut row, &mut col, &mut val, &mut b, &mut cones),
220 num_stationary,
221 &verboser,
222 )?;
223 if verboser.is_enabled(Verbosity::Trace) {
224 crate::verbosity_log!(
225 crate::diag::Verbosity::Summary,
226 "copp3_socp: standard-constraints delta row/col/val/b/cones = +{}/+{}/+{}/+{}/+{}",
227 row.len() - row_before_std,
228 col.len() - col_before_std,
229 val.len() - val_before_std,
230 b.len() - b_before_std,
231 cones.len() - cones_before_std
232 );
233 }
234 let row_before_sqrt = row.len();
237 let col_before_sqrt = col.len();
238 let val_before_sqrt = val.len();
239 let b_before_sqrt = b.len();
240 let cones_before_sqrt = cones.len();
241 let n_var_old = clarabel_sqrt_a_copp3(
242 n,
243 problem.objectives,
244 (&mut row, &mut col, &mut val, &mut b, &mut cones),
245 num_stationary,
246 );
247 if verboser.is_enabled(Verbosity::Trace) {
248 crate::verbosity_log!(
249 crate::diag::Verbosity::Summary,
250 "copp3_socp: sqrt-a stage delta row/col/val/b/cones = +{}/+{}/+{}/+{}/+{}, n_var_old={}",
251 row.len() - row_before_sqrt,
252 col.len() - col_before_sqrt,
253 val.len() - val_before_sqrt,
254 b.len() - b_before_sqrt,
255 cones.len() - cones_before_sqrt,
256 n_var_old
257 );
258 }
259 let mut q_object = Vec::<f64>::with_capacity(n_vars);
260 q_object.resize(n_var_old, 0.0);
261 let row_before_obj = row.len();
263 let col_before_obj = col.len();
264 let val_before_obj = val.len();
265 let b_before_obj = b.len();
266 let cones_before_obj = cones.len();
267 let q_before_obj = q_object.len();
268 clarabel_objective_copp3(
269 problem,
270 num_stationary,
271 (
272 &mut row,
273 &mut col,
274 &mut val,
275 &mut b,
276 &mut cones,
277 &mut q_object,
278 ),
279 )?;
280 if verboser.is_enabled(Verbosity::Trace) {
281 let (q_min, q_max) = q_object
282 .iter()
283 .fold((f64::INFINITY, f64::NEG_INFINITY), |(mn, mx), &v| {
284 (mn.min(v), mx.max(v))
285 });
286 crate::verbosity_log!(
287 crate::diag::Verbosity::Summary,
288 "copp3_socp: objective stage delta row/col/val/b/cones/q = +{}/+{}/+{}/+{}/+{}/+{}, q_range=[{}, {}]",
289 row.len() - row_before_obj,
290 col.len() - col_before_obj,
291 val.len() - val_before_obj,
292 b.len() - b_before_obj,
293 cones.len() - cones_before_obj,
294 q_object.len() - q_before_obj,
295 q_min,
296 q_max
297 );
298 }
299 if verboser.is_enabled(Verbosity::Debug) {
300 crate::verbosity_log!(
301 crate::diag::Verbosity::Summary,
302 "copp3_socp: after objective assembly row={}, col={}, val={}, b={}, cones={}, q={}",
303 row.len(),
304 col.len(),
305 val.len(),
306 b.len(),
307 cones.len(),
308 q_object.len()
309 );
310 }
311 let n_var = q_object.len();
313 let row_len = row.len();
314 let col_len = col.len();
315 let val_len = val.len();
316 let b_len = b.len();
317 let cones_len = cones.len();
318 let a_csc = CscMatrix::new_from_triplets(b.len(), n_var, row, col, val);
319 let p_object = CscMatrix::<f64>::zeros((n_var, n_var));
320 if verboser.is_enabled(Verbosity::Trace) {
321 crate::verbosity_log!(
322 crate::diag::Verbosity::Summary,
323 "copp3_socp: matrix built with m={}, n={}, A.nnz={}, P.nnz={}",
324 b.len(),
325 n_var,
326 a_csc.nnz(),
327 p_object.nnz()
328 );
329 }
330 if verboser.is_enabled(Verbosity::Summary) {
331 crate::verbosity_log!(
332 crate::diag::Verbosity::Summary,
333 "copp3_socp: ready to solve with row/col/val/b/cones = {row_len}/{col_len}/{val_len}/{b_len}/{cones_len} and n_var = {n_var}.",
334 );
335 }
336 let settings = options.clarabel_settings().clone();
338 let mut solver = DefaultSolver::<f64>::new(&p_object, &q_object, &a_csc, &b, &cones, settings)
339 .map_err(|e| CoppError::ClarabelSolverError("copp3_socp".into(), e))?;
340 solver.solve();
341 let solution = solver.solution;
342 if verboser.is_enabled(Verbosity::Summary) {
343 crate::verbosity_log!(
344 crate::diag::Verbosity::Summary,
345 "copp3_socp: solve done, status = {:?}, elapsed = {}.",
346 solution.status,
347 format_duration_human(verboser.elapsed())
348 );
349 }
350 if verboser.is_enabled(Verbosity::Trace) {
351 let show = solution.x.len().min(3);
352 crate::verbosity_log!(
353 crate::diag::Verbosity::Summary,
354 "copp3_socp: solution x_len={}, head={:?}",
355 solution.x.len(),
356 &solution.x[0..show]
357 );
358 }
359 let result = if options.is_allow(solution.status) {
360 let (a, b) =
361 clarabel_to_copp3_solution(&solution.x.as_slice()[0..2 * (n + 1)], &s, num_stationary);
362 Some((a, b, num_stationary))
363 } else {
364 None
365 };
366 if verboser.is_enabled(Verbosity::Trace) {
367 crate::verbosity_log!(
368 crate::diag::Verbosity::Summary,
369 "copp3_socp: allow(status)={}, extracted_profile={}",
370 options.is_allow(solution.status),
371 if result.is_some() {
372 "Some((a,b,num_stationary))"
373 } else {
374 "None"
375 }
376 );
377 }
378 Ok((result, solution))
379}
380
381#[inline(always)]
383fn length_xi(n: usize, num_stationary: (usize, usize)) -> usize {
384 n + 1 - num_stationary.0.max(1) - num_stationary.1.max(1)
385}
386
387#[inline(always)]
389fn skip_a_for_xi(num_stationary_start: usize) -> usize {
390 num_stationary_start.max(1)
391}
392
393fn clarabel_sqrt_a_copp3(
399 n: usize,
400 objective: &[CoppObjective],
401 constraints: ConstraintsClarabel,
402 num_stationary: (usize, usize),
403) -> usize {
404 let (row, col, val, b, cones) = constraints;
405 for obj in objective {
406 match obj {
407 CoppObjective::Time(_) | CoppObjective::ThermalEnergy(_, _) => {
408 let n_skip = skip_a_for_xi(num_stationary.0);
409 let len_xi = length_xi(n, num_stationary); row.extend(b.len()..b.len() + len_xi);
413 col.extend((2 * (n + 1))..(2 * (n + 1) + len_xi));
414 val.resize(val.len() + len_xi, -1.0);
415 b.resize(b.len() + len_xi, 0.0);
416 cones.push(NonnegativeConeT(len_xi));
417 row.extend(b.len()..b.len() + 3 * len_xi);
423 val.resize(val.len() + 3 * len_xi, -1.0);
424 cones.resize(cones.len() + len_xi, SecondOrderConeT(3));
425 for k in 0..len_xi {
426 col.extend([k + n_skip, k + n_skip, 2 * (n + 1) + k]);
428 b.extend([0.25, -0.25, 0.0]);
430 }
432 return 2 * (n + 1) + len_xi;
433 }
434 _ => {}
435 }
436 }
437 2 * (n + 1)
438}
439
440fn clarabel_objective_capacity_copp3<M: RobotBasic>(
442 n: usize,
443 objective: &[CoppObjective],
444 robot: &Robot<M>,
445) -> (usize, usize, usize, usize) {
446 let (mut capacity_val, mut capacity_b, mut capacity_cones, mut n_vars) =
449 if objective.iter().any(|obj| {
450 matches!(
451 obj,
452 CoppObjective::Time(_) | CoppObjective::ThermalEnergy(_, _)
453 )
454 }) {
455 (4 * n, 4 * n, n, 2 * (n + 1))
456 } else {
457 (0, 0, 0, 2 * (n + 1))
458 };
459 let dim = robot.dim();
461 for obj in objective {
462 match obj {
463 CoppObjective::Time(_) => {
464 capacity_val += 5 * n;
466 capacity_b += 4 * n;
467 capacity_cones += n;
468 n_vars += n;
469 }
470 CoppObjective::ThermalEnergy(_, _) => {
471 capacity_val += (4 + 2 * dim) * (n + 1);
473 capacity_b += (dim + 2) * (n + 1);
474 capacity_cones += n + 1;
475 n_vars += n + 1;
476 }
477 CoppObjective::TotalVariationTorque(_, _) => {
478 capacity_val += 10 * dim * n;
480 capacity_b += 2 * dim * n;
481 capacity_cones += 1;
482 n_vars += dim * n;
483 }
484 _ => {}
485 }
486 }
487 (capacity_val, capacity_b, capacity_cones, n_vars)
488}
489
490fn clarabel_objective_copp3<M: RobotTorque>(
491 problem: &Copp3Problem<M>,
492 num_stationary: (usize, usize),
493 objective_constraints: ObjConsClarabel,
494) -> Result<(), CoppError> {
495 let (row, col, val, b, cones, q_object) = objective_constraints;
496 let n = problem.a_linearization.len() - 1;
497 let s = problem
498 .robot
499 .constraints
500 .s_vec(problem.idx_s_start, problem.idx_s_start + n + 1)?;
501 let weight_a_time = if problem
502 .objectives
503 .iter()
504 .any(|obj| matches!(obj, CoppObjective::Time(_)))
505 {
506 get_weight_a_topp3(&s, num_stationary)
507 } else {
508 vec![]
509 };
510 let weight_a_torque = if problem
511 .objectives
512 .iter()
513 .any(|obj| matches!(obj, CoppObjective::ThermalEnergy(_, _)))
514 {
515 get_weight_a_copp3(&s, num_stationary)
516 } else {
517 vec![]
518 };
519 let coeffs_torque = if problem.objectives.iter().any(|obj| {
520 matches!(
521 obj,
522 CoppObjective::ThermalEnergy(_, _) | CoppObjective::TotalVariationTorque(_, _)
523 )
524 }) {
525 problem.robot.torque_coeff(problem.idx_s_start, n + 1)
527 } else {
528 (
529 DMatrix::<f64>::zeros(0, 0),
530 DMatrix::<f64>::zeros(0, 0),
531 DMatrix::<f64>::zeros(0, 0),
532 )
533 };
534 for obj in problem.objectives {
535 match obj {
536 CoppObjective::Time(weight) => {
537 if !clarabel_objective_time_copp3(
538 &s,
539 *weight,
540 &weight_a_time,
541 num_stationary,
542 (row, col, val, b, cones, q_object),
543 ) {
544 return Err(CoppError::InvalidInput(
545 "clarabel_objective_copp3".into(),
546 "Invalid Time objective".into(),
547 ));
548 }
549 }
550 CoppObjective::ThermalEnergy(weight, normalize) => {
551 if !clarabel_objective_thermal_energy_copp3(
552 &weight_a_torque,
553 *weight,
554 normalize,
555 &coeffs_torque,
556 num_stationary,
557 (row, col, val, b, cones, q_object),
558 ) {
559 return Err(CoppError::InvalidInput(
560 "clarabel_objective_copp3".into(),
561 "Invalid ThermalEnergy objective".into(),
562 ));
563 }
564 }
565 CoppObjective::TotalVariationTorque(weight, normalize) => {
566 if !clarabel_objective_tv_torque_copp3(
567 *weight,
568 normalize,
569 &coeffs_torque,
570 num_stationary,
571 (row, col, val, b, cones, q_object),
572 ) {
573 return Err(CoppError::InvalidInput(
574 "clarabel_objective_copp3".into(),
575 "Invalid TotalVariationTorque objective".into(),
576 ));
577 }
578 }
579 CoppObjective::Linear(weight, alpha, beta) => {
580 if !clarabel_objective_linear_copp3(
581 &s,
582 *weight,
583 alpha,
584 beta,
585 q_object,
586 num_stationary,
587 ) {
588 return Err(CoppError::InvalidInput(
589 "clarabel_objective_copp3".into(),
590 "Invalid Linear objective".into(),
591 ));
592 }
593 }
594 }
595 }
596 Ok(())
597}
598
599fn clarabel_objective_time_copp3(
602 s: &[f64],
603 weight: f64,
604 weight_a: &[f64],
605 num_stationary: (usize, usize),
606 objective_constraints: ObjConsClarabel,
607) -> bool {
608 if weight < 0.0 {
609 return false;
610 }
611
612 let (row, col, val, b, cones, q_object) = objective_constraints;
613 let n = s.len() - 1;
614 let len_xi = length_xi(n, num_stationary);
615 let k_skip = skip_a_for_xi(num_stationary.0);
616 let id_xi_start = 2 * (n + 1);
620 let id_eta_start = q_object.len();
621 row.extend(b.len()..(b.len() + len_xi));
624 col.extend(id_eta_start..(id_eta_start + len_xi));
625 val.resize(val.len() + len_xi, -1.0);
626 b.resize(b.len() + len_xi, 0.0);
627 cones.push(NonnegativeConeT(len_xi));
628 for k in 0..len_xi {
631 col.extend([
633 id_xi_start + k,
634 id_eta_start + k,
635 id_xi_start + k,
636 id_eta_start + k,
637 ]);
638 row.extend([b.len(), b.len(), b.len() + 1, b.len() + 1]);
639 val.extend([-1.0, -1.0, -1.0, 1.0]);
640 b.extend([0.0, 0.0, 2.0]);
641 }
642 cones.resize(cones.len() + len_xi, SecondOrderConeT(3));
643 q_object.extend(
646 weight_a
647 .iter()
648 .skip(k_skip)
649 .take(len_xi)
650 .map(|w| weight * w),
651 );
652
653 true
654}
655
656fn clarabel_objective_thermal_energy_copp3(
659 weight_a: &[f64],
660 weight: f64,
661 normalize: &[f64],
662 coeffs_torque: &(DMatrix<f64>, DMatrix<f64>, DMatrix<f64>),
663 num_stationary: (usize, usize),
664 objective_constraints: ObjConsClarabel,
665) -> bool {
666 if weight < 0.0 {
667 return false;
668 }
669 let (row, col, val, b, cones, q_object) = objective_constraints;
670 let mut coeff_a = coeffs_torque.0.clone();
672 let mut coeff_b = coeffs_torque.1.clone();
673 let mut coeff_g = coeffs_torque.2.clone();
674 let dim = coeff_a.nrows();
675 if normalize.len() != dim {
676 return false;
677 }
678 let n = coeff_a.ncols() - 1;
679 let normalize = DVectorView::from_slice(normalize, dim);
681 for mut col in coeff_a.column_iter_mut() {
682 col.component_mul_assign(&normalize);
683 }
684 for mut col in coeff_b.column_iter_mut() {
685 col.component_mul_assign(&normalize);
686 }
687 for mut col in coeff_g.column_iter_mut() {
688 col.component_mul_assign(&normalize);
689 }
690 let k_skip = skip_a_for_xi(num_stationary.0);
692 let len_xi = length_xi(n, num_stationary);
693
694 if num_stationary.0 > 0 {
695 let col_a = coeff_a.column(num_stationary.0);
701 let col_b = coeff_b.column(num_stationary.0);
702 let col_g = coeff_g.column(num_stationary.0);
703 let col_g_0 = coeff_g.column(0);
704 let id_t = q_object.len();
710 row.extend(b.len()..(b.len() + 2));
712 col.resize(col.len() + 2, id_t);
713 row.extend(b.len()..(b.len() + 2));
714 col.resize(col.len() + 2, 2 * (n + 1));
715 val.resize(val.len() + 3, -1.0);
716 val.push(1.0);
717 b.resize(b.len() + 2, 0.0);
718 row.extend((b.len())..(b.len() + dim));
720 col.resize(col.len() + dim, num_stationary.0);
721 val.extend(col_a.iter().take(dim));
722 row.extend((b.len())..(b.len() + dim));
723 col.resize(col.len() + dim, n + 1 + num_stationary.0);
724 val.extend(col_b.iter().take(dim));
725 b.extend(
726 col_g
727 .iter()
728 .zip(col_g_0.iter())
729 .take(dim)
730 .map(|(&g, &g_0)| -(g + g_0)),
731 );
732 cones.push(SecondOrderConeT(dim + 2));
733 q_object.push(weight * weight_a[0]);
734 }
735 if num_stationary.1 > 0 {
736 let col_a = coeff_a.column(n - num_stationary.1);
742 let col_b = coeff_b.column(n - num_stationary.1);
743 let col_g = coeff_g.column(n - num_stationary.1);
744 let col_g_f = coeff_g.column(n);
745 let id_t = q_object.len();
751 row.extend(b.len()..(b.len() + 2));
753 col.resize(col.len() + 2, id_t);
754 row.extend(b.len()..(b.len() + 2));
755 col.resize(col.len() + 2, 2 * n + len_xi + 1);
756 val.resize(val.len() + 3, -1.0);
757 val.push(1.0);
758 b.resize(b.len() + 2, 0.0);
759 row.extend((b.len())..(b.len() + dim));
761 col.resize(col.len() + dim, n - num_stationary.1);
762 val.extend(col_a.iter().take(dim));
763 row.extend((b.len())..(b.len() + dim));
764 col.resize(col.len() + dim, 2 * n + 1 - num_stationary.1);
765 val.extend(col_b.iter().take(dim));
766 b.extend(
767 col_g
768 .iter()
769 .zip(col_g_f.iter())
770 .take(dim)
771 .map(|(&g, &g_f)| -(g + g_f)),
772 );
773 cones.push(SecondOrderConeT(dim + 2));
774 q_object.push(weight * weight_a[n]);
775 }
776
777 let id_t_start = q_object.len();
781 for (k, (col_a, col_b, col_g)) in izip!(
782 coeff_a.column_iter(),
783 coeff_b.column_iter(),
784 coeff_g.column_iter()
785 )
786 .skip(k_skip)
787 .take(len_xi)
788 .enumerate()
789 {
790 row.extend(b.len()..(b.len() + 2));
795 col.resize(col.len() + 2, id_t_start + k);
796 row.extend(b.len()..(b.len() + 2));
797 col.resize(col.len() + 2, 2 * (n + 1) + k);
798 val.resize(val.len() + 3, -1.0);
799 val.push(1.0);
800 b.resize(b.len() + 2, 0.0);
801 row.extend((b.len())..(b.len() + dim));
803 col.resize(col.len() + dim, k + k_skip);
804 val.extend(col_a.iter().take(dim));
805 row.extend((b.len())..(b.len() + dim));
806 col.resize(col.len() + dim, n + 1 + k + k_skip);
807 val.extend(col_b.iter().take(dim));
808 b.extend(col_g.iter().take(dim).map(|&g| -g));
809 }
810 cones.resize(cones.len() + len_xi, SecondOrderConeT(dim + 2));
811 let weight_four = 4.0 * weight;
815 q_object.extend(
816 weight_a
817 .iter()
818 .skip(k_skip)
819 .take(len_xi)
820 .map(|w| weight_four * w),
821 );
822 true
823}
824
825fn clarabel_objective_tv_torque_copp3(
828 weight: f64,
829 normalize: &[f64],
830 coeffs_torque: &(DMatrix<f64>, DMatrix<f64>, DMatrix<f64>),
831 num_stationary: (usize, usize),
832 objective_constraints: ObjConsClarabel,
833) -> bool {
834 if weight < 0.0 {
835 return false;
836 }
837 let (row, col, val, b, cones, q_object) = objective_constraints;
838 let mut coeff_a = coeffs_torque.0.clone();
841 let mut coeff_b = coeffs_torque.1.clone();
842 let mut coeff_g = coeffs_torque.2.clone();
843 let dim = coeff_a.nrows();
844 if normalize.len() != dim {
845 return false;
846 }
847 let n = coeff_a.ncols() - 1;
848 let normalize = DVectorView::from_slice(normalize, dim);
850 for mut col in coeff_a.column_iter_mut() {
851 col.component_mul_assign(&normalize);
852 }
853 for mut col in coeff_b.column_iter_mut() {
854 col.component_mul_assign(&normalize);
855 }
856 for mut col in coeff_g.column_iter_mut() {
857 col.component_mul_assign(&normalize);
858 }
859 let n_b_old = b.len();
863 if num_stationary.0 > 0 {
866 let col_a = coeff_a.column(num_stationary.0);
868 let col_b = coeff_b.column(num_stationary.0);
869 let col_g = coeff_g.column(num_stationary.0);
870 let col_g_0 = coeff_g.column(0);
871 let n_var_old = q_object.len();
872 for (i, (&v_a, &v_b, &v_g, &v_g_0)) in
873 izip!(col_a.iter(), col_b.iter(), col_g.iter(), col_g_0.iter())
874 .take(dim)
875 .enumerate()
876 {
877 row.resize(row.len() + 3, b.len());
880 col.extend([num_stationary.0, n + num_stationary.0 + 1, n_var_old + i]);
881 val.extend([v_a, v_b, -1.0]);
882 b.push(v_g - v_g_0);
883 row.resize(row.len() + 3, b.len());
885 col.extend([num_stationary.0, n + num_stationary.0 + 1, n_var_old + i]);
886 val.extend([-v_a, -v_b, -1.0]);
887 b.push(v_g_0 - v_g);
888 }
889 q_object.resize(q_object.len() + dim, weight);
890 }
891
892 if num_stationary.1 > 0 {
893 let col_a = coeff_a.column(n - num_stationary.1);
895 let col_b = coeff_b.column(n - num_stationary.1);
896 let col_g = coeff_g.column(n - num_stationary.1);
897 let col_g_f = coeff_g.column(n);
898 let n_var_old = q_object.len();
899 for (i, (&v_a, &v_b, &v_g, &v_g_f)) in
900 izip!(col_a.iter(), col_b.iter(), col_g.iter(), col_g_f.iter())
901 .take(dim)
902 .enumerate()
903 {
904 row.resize(row.len() + 3, b.len());
907 col.extend([
908 n - num_stationary.1,
909 n + n - num_stationary.1 + 1,
910 n_var_old + i,
911 ]);
912 val.extend([v_a, v_b, -1.0]);
913 b.push(v_g - v_g_f);
914 row.resize(row.len() + 3, b.len());
916 col.extend([
917 n - num_stationary.1,
918 n + n - num_stationary.1 + 1,
919 n_var_old + i,
920 ]);
921 val.extend([-v_a, -v_b, -1.0]);
922 b.push(v_g_f - v_g);
923 }
924 q_object.resize(q_object.len() + dim, weight);
925 }
926
927 for (k, ((col_a_curr, col_b_curr, col_g_curr), (col_a_next, col_b_next, col_g_next))) in izip!(
929 coeff_a.column_iter(),
930 coeff_b.column_iter(),
931 coeff_g.column_iter()
932 )
933 .tuple_windows()
934 .enumerate()
935 .skip(num_stationary.0)
936 .take(n - num_stationary.0 - num_stationary.1)
937 {
938 let n_var_old = q_object.len();
939 for (i, (&v_a_curr, &v_b_curr, &v_g_curr, &v_a_next, &v_b_next, &v_g_next)) in izip!(
940 col_a_curr.iter(),
941 col_b_curr.iter(),
942 col_g_curr.iter(),
943 col_a_next.iter(),
944 col_b_next.iter(),
945 col_g_next.iter()
946 )
947 .enumerate()
948 {
949 row.resize(row.len() + 5, b.len());
952 col.extend([k, k + 1, n + k + 1, n + k + 2, n_var_old + i]);
953 val.extend([-v_a_curr, v_a_next, -v_b_curr, v_b_next, -1.0]);
954 b.push(v_g_next - v_g_curr);
955 row.resize(row.len() + 5, b.len());
957 col.extend([k, k + 1, n + k + 1, n + k + 2, n_var_old + i]);
958 val.extend([v_a_curr, -v_a_next, v_b_curr, -v_b_next, -1.0]);
959 b.push(v_g_curr - v_g_next);
960 }
961 q_object.resize(q_object.len() + dim, weight);
962 }
963 cones.push(NonnegativeConeT(b.len() - n_b_old));
964
965 true
966}
967
968fn clarabel_objective_linear_copp3(
970 s: &[f64],
971 weight: f64,
972 alpha: &[f64],
973 beta: &[f64],
974 q_object: &mut [f64],
975 num_stationary: (usize, usize),
976) -> bool {
977 if alpha.len() != s.len() || beta.len() != s.len() {
978 return false;
979 }
980 let n = s.len() - 1;
981 if num_stationary.0 > 1 {
983 let &s_start = s.first().unwrap();
984 let ds_start = s[num_stationary.0] - s_start;
985 let q_n1 = &mut q_object[num_stationary.0];
986 for (&s_k, &alpha_k, &beta_k) in izip!(s.iter(), alpha.iter(), beta.iter())
987 .skip(1)
988 .take(num_stationary.0 - 1)
989 {
990 let dsk_start = s_k - s_start;
991 let gamma = dsk_start / ds_start;
992 *q_n1 += weight * gamma * gamma.cbrt() * (alpha_k + beta_k / (1.5 * dsk_start));
995 }
996 }
997 if num_stationary.1 > 1 {
998 let &s_final = s.last().unwrap();
999 let ds_final = s[n - num_stationary.1] - s_final;
1000 let q_n2 = &mut q_object[n - num_stationary.1];
1001 for (&s_k, &alpha_k, &beta_k) in izip!(s.iter(), alpha.iter(), beta.iter())
1002 .skip(1)
1003 .take(num_stationary.1 - 1)
1004 {
1005 let dsk_final = s_k - s_final;
1006 let gamma = dsk_final / ds_final;
1007 *q_n2 += weight * gamma * gamma.cbrt() * (alpha_k + beta_k / (1.5 * dsk_final));
1010 }
1011 }
1012 for (q_k, &alpha_k) in q_object
1013 .iter_mut()
1014 .zip(alpha.iter())
1015 .take(n + 1 - num_stationary.1)
1016 .skip(num_stationary.0)
1017 {
1018 *q_k += weight * alpha_k;
1019 }
1020 for (q_k, &beta_k) in q_object
1021 .iter_mut()
1022 .skip(n + 1)
1023 .zip(beta.iter())
1024 .take(n + 1 - num_stationary.1)
1025 .skip(num_stationary.0)
1026 {
1027 *q_k += weight * beta_k;
1028 }
1029
1030 true
1031}
1032
1033#[cfg(test)]
1036#[inline(always)]
1037fn objective_value_time_copp3(
1038 a_sqrt_down: &[f64],
1039 weight_a: &[f64],
1040 num_stationary: (usize, usize),
1041) -> f64 {
1042 let n = a_sqrt_down.len() - 1;
1043 let k_skip = skip_a_for_xi(num_stationary.0);
1044 let len_xi = length_xi(n, num_stationary);
1045 let mut objective = 0.0;
1047 for (a_sqrt_down, weight_a) in a_sqrt_down
1048 .iter()
1049 .zip(weight_a.iter())
1050 .skip(k_skip)
1051 .take(len_xi)
1052 {
1053 objective += weight_a * a_sqrt_down;
1054 }
1055 objective
1056}
1057
1058#[cfg(test)]
1060#[inline(always)]
1061fn objective_value_thermal_energy_copp3(
1062 a_sqrt_down: &[f64],
1063 weight_a: &[f64],
1064 num_stationary: (usize, usize),
1065 torque: &DMatrix<f64>,
1066 normalize: &[f64],
1067) -> f64 {
1068 let mut objective = 0.0;
1069 let n = a_sqrt_down.len() - 1;
1070 if num_stationary.0 > 0 {
1071 let torque_n1 = torque.column(num_stationary.0);
1074 let torque_0 = torque.column(0);
1075 let weight = weight_a[0] * a_sqrt_down[num_stationary.0];
1076 for (tau_n1, tau_0, &normalize_i) in
1077 izip!(torque_n1.iter(), torque_0.iter(), normalize.iter())
1078 {
1079 let tau_average = 0.5 * normalize_i * (tau_n1 + tau_0);
1080 objective += weight * tau_average * tau_average;
1081 }
1082 }
1083 if num_stationary.1 > 0 {
1084 let torque_n2 = torque.column(n - num_stationary.1);
1087 let torque_n = torque.column(n);
1088 let weight = weight_a[n] * a_sqrt_down[n - num_stationary.1];
1089 for (tau_n2, tau_n, &normalize_i) in
1090 izip!(torque_n2.iter(), torque_n.iter(), normalize.iter())
1091 {
1092 let tau_average = 0.5 * normalize_i * (tau_n2 + tau_n);
1093 objective += weight * tau_average * tau_average;
1094 }
1095 }
1096 let k_skip = skip_a_for_xi(num_stationary.0);
1097 let len_xi = length_xi(n, num_stationary);
1098 for (torque_k, &a_sqrt_down_k, &weight_a_k) in
1099 izip!(torque.column_iter(), a_sqrt_down.iter(), weight_a.iter())
1100 .skip(k_skip)
1101 .take(len_xi)
1102 {
1103 for (tau_k, &normalize_i) in torque_k.iter().zip(normalize.iter()) {
1104 let tau_normal = tau_k * normalize_i;
1105 objective += 4.0 * weight_a_k * tau_normal * tau_normal * a_sqrt_down_k;
1106 }
1107 }
1108
1109 objective
1110}
1111
1112#[cfg(test)]
1114#[inline(always)]
1115fn objective_value_tv_torque_copp3(
1116 torque: &DMatrix<f64>,
1117 normalize: &[f64],
1118 num_stationary: (usize, usize),
1119) -> f64 {
1120 let mut objective = 0.0;
1121 let n = torque.ncols() - 1;
1122 if num_stationary.0 > 0 {
1123 let torque_n1 = torque.column(num_stationary.0);
1125 let torque_0 = torque.column(0);
1126 for (tau_n1, tau_0, &normalize_i) in
1127 izip!(torque_n1.iter(), torque_0.iter(), normalize.iter())
1128 {
1129 objective += normalize_i * (tau_n1 - tau_0).abs();
1130 }
1131 }
1132 if num_stationary.1 > 0 {
1133 let torque_n2 = torque.column(n - num_stationary.1);
1135 let torque_n = torque.column(n);
1136 for (tau_n2, tau_n, &normalize_i) in
1137 izip!(torque_n2.iter(), torque_n.iter(), normalize.iter())
1138 {
1139 objective += normalize_i * (tau_n2 - tau_n).abs();
1140 }
1141 }
1142 for (torque_col_curr, torque_col_next) in torque
1144 .column_iter()
1145 .tuple_windows()
1146 .skip(num_stationary.0)
1147 .take(n - num_stationary.0 - num_stationary.1)
1148 {
1149 for (tau_k, tau_k_next, &normalize_i) in izip!(
1150 torque_col_curr.iter(),
1151 torque_col_next.iter(),
1152 normalize.iter()
1153 ) {
1154 objective += normalize_i * (tau_k_next - tau_k).abs();
1155 }
1156 }
1157
1158 objective
1159}
1160
1161#[cfg(test)]
1163#[inline(always)]
1164fn objective_value_linear_copp3(
1165 a_profile: &[f64],
1166 b_profile: &[f64],
1167 alpha: &[f64],
1168 beta: &[f64],
1169) -> f64 {
1170 let mut objective = 0.0;
1172 for (a_curr, alpha_curr) in a_profile.iter().zip(alpha.iter()) {
1173 objective += a_curr * alpha_curr;
1175 }
1176 for (b_curr, beta_curr) in b_profile.iter().zip(beta.iter()) {
1177 objective += b_curr * beta_curr;
1179 }
1180 objective
1181}
1182
1183#[cfg(test)]
1185pub(crate) fn objective_value_copp3_opt<M: RobotTorque>(
1186 problem: &Copp3Problem<M>,
1187 a_profile: &[f64],
1188 b_profile: &[f64],
1189 num_stationary: (usize, usize),
1190) -> (f64, Vec<f64>) {
1191 let s = problem
1192 .robot
1193 .constraints
1194 .s_vec(problem.idx_s_start, problem.idx_s_start + a_profile.len());
1195 let Ok(s) = s else {
1196 return (f64::INFINITY, vec![0.0; problem.objectives.len()]);
1197 };
1198 if a_profile.len() != s.len() || b_profile.len() != s.len() {
1199 return (f64::INFINITY, vec![0.0; problem.objectives.len()]);
1200 }
1201 let (a_sqrt_down, weight_a_time) = if problem.objectives.iter().any(|obj| {
1202 matches!(
1203 obj,
1204 CoppObjective::Time(_) | CoppObjective::ThermalEnergy(_, _)
1205 )
1206 }) {
1207 (
1208 a_profile
1209 .iter()
1210 .map(|a| 1.0 / a.sqrt().max(1E-16))
1211 .collect(),
1212 get_weight_a_topp3(&s, num_stationary),
1213 )
1214 } else {
1215 (vec![], vec![])
1216 };
1217 let weight_a_torque = if problem
1218 .objectives
1219 .iter()
1220 .any(|obj| matches!(obj, CoppObjective::ThermalEnergy(_, _)))
1221 {
1222 get_weight_a_copp3(&s, num_stationary)
1223 } else {
1224 vec![]
1225 };
1226 let torque = if problem.objectives.iter().any(|obj| {
1227 matches!(
1228 obj,
1229 CoppObjective::ThermalEnergy(_, _) | CoppObjective::TotalVariationTorque(_, _)
1230 )
1231 }) {
1232 let torque_result =
1233 problem
1234 .robot
1235 .get_torque_with_ab(a_profile, b_profile, problem.idx_s_start);
1236 match torque_result {
1237 Ok(torque) => torque,
1238 _ => return (f64::INFINITY, vec![0.0; problem.objectives.len()]),
1239 }
1240 } else {
1241 DMatrix::<f64>::zeros(0, 0)
1242 };
1243 let mut obj_val = Vec::with_capacity(problem.objectives.len());
1244 let mut obj_val_total = 0.0;
1245 for obj in problem.objectives {
1246 match obj {
1247 CoppObjective::Time(weight) => {
1248 let obj_here =
1249 objective_value_time_copp3(&a_sqrt_down, &weight_a_time, num_stationary);
1250 obj_val.push(obj_here);
1251 obj_val_total += weight * obj_here;
1252 }
1253 CoppObjective::ThermalEnergy(weight, normalize) => {
1254 let obj_here = objective_value_thermal_energy_copp3(
1255 &a_sqrt_down,
1256 &weight_a_torque,
1257 num_stationary,
1258 &torque,
1259 normalize,
1260 );
1261 obj_val.push(obj_here);
1262 obj_val_total += weight * obj_here;
1263 }
1264 CoppObjective::TotalVariationTorque(weight, normalize) => {
1265 let obj_here = objective_value_tv_torque_copp3(&torque, normalize, num_stationary);
1266 obj_val.push(obj_here);
1267 obj_val_total += weight * obj_here;
1268 }
1269 CoppObjective::Linear(weight, alpha, beta) => {
1270 let obj_here = objective_value_linear_copp3(a_profile, b_profile, alpha, beta);
1271 obj_val.push(obj_here);
1272 obj_val_total += weight * obj_here;
1273 }
1274 }
1275 }
1276 (obj_val_total, obj_val)
1277}
1278
1279#[cfg(test)]
1280mod tests {
1281 use super::*;
1282 use crate::copp::copp2::stable::basic::{Topp2ProblemBuilder, s_to_t_topp2};
1283 use crate::copp::copp2::stable::reach_set2::ReachSet2OptionsBuilder;
1284 use crate::copp::copp2::stable::topp2_ra::topp2_ra;
1285 use crate::copp::copp3::stable::basic::{Copp3ProblemBuilder, s_to_t_topp3};
1286 use crate::copp::copp3::stable::topp3_lp::topp3_lp;
1287 use crate::copp::copp3::stable::topp3_socp::topp3_socp;
1288 use crate::copp::{ClarabelOptionsBuilder, default_clarabel_settings};
1289 use crate::path::{add_symmetric_axial_limits_for_test, lissajous_path_for_test};
1290 use crate::robot::robot_core::Robot;
1291 use std::time::Instant;
1292 use std::vec;
1293
1294 #[test]
1295 fn test_copp3_lp() -> Result<(), CoppError> {
1296 run_test_copp3_lp_repeated(1, false)
1297 }
1298
1299 #[test]
1302 #[ignore = "slow"]
1303 fn test_copp3_lp_robust() -> Result<(), CoppError> {
1304 run_test_copp3_lp_repeated(100, true)
1305 }
1306
1307 #[test]
1308 fn test_copp3_qp() -> Result<(), CoppError> {
1309 run_test_copp3_qp_repeated(1, false)
1310 }
1311
1312 #[test]
1315 #[ignore = "slow"]
1316 fn test_copp3_qp_robust() -> Result<(), CoppError> {
1317 run_test_copp3_qp_repeated(100, true)
1318 }
1319
1320 #[test]
1321 fn test_all_objectives() -> Result<(), CoppError> {
1322 run_test_all_objectives_repeated(1, false)
1323 }
1324
1325 #[test]
1333 #[ignore = "slow"]
1334 fn test_all_objectives_robust() -> Result<(), CoppError> {
1335 run_test_all_objectives_repeated(100, true)
1336 }
1337
1338 fn run_test_copp3_lp_repeated(n_exp: usize, flag_print_step: bool) -> Result<(), CoppError> {
1339 let mut tc_sum_ra = 0.0;
1340 let mut tc_sum_lp = 0.0;
1341 let mut tc_sum_copp = 0.0;
1342 let mut tf_sum_ra = 0.0;
1343 let mut tf_sum_lp = 0.0;
1344 let mut tf_sum_copp = 0.0;
1345 let mut obj_sum_lp = 0.0;
1346 let mut obj_sum_copp = 0.0;
1347
1348 for i_exp in 0..n_exp {
1349 let n: usize = 1000;
1350 let dim = 7;
1351 let mut robot = Robot::with_capacity(dim, n);
1352
1353 let mut rng = rand::rng();
1354 let (s, derivs, omega, phi) =
1355 lissajous_path_for_test(dim, n, &mut rng).expect("random range is valid");
1356 robot.with_s(&s.as_view())?;
1357 robot.with_q(
1358 &derivs.q.as_view(),
1359 &derivs.dq.as_ref().unwrap().as_view(),
1360 &derivs.ddq.as_ref().unwrap().as_view(),
1361 derivs.dddq.as_ref().map(|m| m.as_view()).as_ref(),
1362 0,
1363 )?;
1364 add_symmetric_axial_limits_for_test(&mut robot, 1.0, 1.0, Some(5.0))?;
1365
1366 let topp2_problem = Topp2ProblemBuilder::new(&robot, (0, n - 1), (0.0, 0.0)).build()?;
1367 let start = Instant::now();
1369 let options_ra0 = ReachSet2OptionsBuilder::new()
1370 .lp_feas_tol(1E-9)
1371 .a_cmp_abs_tol(1E-9)
1372 .a_cmp_rel_tol(1E-9)
1373 .build()?;
1374 let a_ra0 = topp2_ra(&topp2_problem, &options_ra0)?;
1375 let tc_ra0 = start.elapsed().as_secs_f64() * 1E3;
1376 let (tf_ra0, _) = s_to_t_topp2(s.as_slice(), &a_ra0, 0.0);
1377
1378 let objectives = [CoppObjective::Linear(
1379 1.0,
1380 &get_weight_a_topp3(&s.as_slice()[0..n], (1, 1))
1381 .iter()
1382 .map(|&w_a| -w_a)
1383 .collect_vec(),
1384 &vec![0.0; n],
1385 )];
1386 let copp3_problem = Copp3ProblemBuilder::new(
1387 &mut robot,
1388 &objectives,
1389 0,
1390 &a_ra0,
1391 (0.0, 0.0),
1392 (0.0, 0.0),
1393 )
1394 .build_with_linearization()?;
1395
1396 let start = Instant::now();
1398 let (a_copp, b_copp, num_stationary_copp) = {
1399 let mut settings = default_clarabel_settings();
1400 settings.tol_gap_rel = 1E-6;
1401 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1402 .allow_almost_solved(true)
1403 .build()?;
1404 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1405 if let Some(result) = result {
1406 result
1407 } else {
1408 return Err(CoppError::ClarabelSolverStatus(
1409 "copp3_socp".into(),
1410 solution.status,
1411 ));
1412 }
1413 };
1414 let tc_copp = start.elapsed().as_secs_f64() * 1E3;
1415 let (tf_copp, _) =
1417 s_to_t_topp3(s.as_slice(), &a_copp, &b_copp, num_stationary_copp, 0.0);
1418
1419 let options_lp = ClarabelOptionsBuilder::new()
1421 .allow_almost_solved(true)
1422 .build()?;
1423 let start = Instant::now();
1424 let (a_lp, b_lp, _) = topp3_lp(&copp3_problem.as_topp3_problem(), &options_lp)?;
1425 let tc_lp = start.elapsed().as_secs_f64() * 1E3;
1426 let (tf_lp, _) = s_to_t_topp3(s.as_slice(), &a_lp, &b_lp, num_stationary_copp, 0.0);
1428
1429 let (obj_lp, _) =
1430 objective_value_copp3_opt(&copp3_problem, &a_lp, &b_lp, num_stationary_copp);
1431 let (obj_copp, _) =
1432 objective_value_copp3_opt(&copp3_problem, &a_copp, &b_copp, num_stationary_copp);
1433
1434 if flag_print_step {
1435 crate::verbosity_log!(
1436 crate::diag::Verbosity::Summary,
1437 "Exp #{}: tc_ra = {:.4} ms, tc_lp = {:.4} ms, tc_copp = {:.4} ms, tf_ra = {:.6}, tf_lp = {:.6}, tf_copp = {:.6}, obj_lp = {:.6}, obj_copp = {:.6}",
1438 i_exp + 1,
1439 tc_ra0,
1440 tc_lp,
1441 tc_copp,
1442 tf_ra0,
1443 tf_lp,
1444 tf_copp,
1445 obj_lp,
1446 obj_copp
1447 );
1448 }
1449
1450 if (tf_copp - tf_lp).abs() > 1e-8 {
1451 crate::verbosity_log!(
1452 crate::diag::Verbosity::Summary,
1453 "omega = {omega:?}\nphi = {phi:?}"
1454 );
1455 crate::verbosity_log!(
1456 crate::diag::Verbosity::Debug,
1457 "COPP3 time optimality failed! tf_copp - tf_lp = {}",
1458 tf_copp - tf_lp
1459 );
1460 }
1461
1462 tc_sum_ra += tc_ra0;
1463 tc_sum_lp += tc_lp;
1464 tc_sum_copp += tc_copp;
1465 tf_sum_ra += tf_ra0;
1466 tf_sum_lp += tf_lp;
1467 tf_sum_copp += tf_copp;
1468 obj_sum_lp += obj_lp;
1469 obj_sum_copp += obj_copp;
1470 }
1471
1472 crate::verbosity_log!(
1473 crate::diag::Verbosity::Summary,
1474 "Average {} experiments: tc_ra = {:.4} ms, tc_lp = {:.4} ms, tc_copp = {:.4} ms, tf_ra = {:.6}, tf_lp = {:.6}, tf_copp = {:.6}, obj_lp = {:.6}, obj_copp = {:.6}",
1475 n_exp,
1476 tc_sum_ra / n_exp as f64,
1477 tc_sum_lp / n_exp as f64,
1478 tc_sum_copp / n_exp as f64,
1479 tf_sum_ra / n_exp as f64,
1480 tf_sum_lp / n_exp as f64,
1481 tf_sum_copp / n_exp as f64,
1482 obj_sum_lp / n_exp as f64,
1483 obj_sum_copp / n_exp as f64
1484 );
1485
1486 Ok(())
1487 }
1488
1489 fn run_test_copp3_qp_repeated(n_exp: usize, flag_print_step: bool) -> Result<(), CoppError> {
1490 let mut tc_sum_ra = 0.0;
1491 let mut tc_sum_qp = 0.0;
1492 let mut tc_sum_copp = 0.0;
1493 let mut tf_sum_ra = 0.0;
1494 let mut tf_sum_qp = 0.0;
1495 let mut tf_sum_copp = 0.0;
1496 let mut obj_sum_qp = 0.0;
1497 let mut obj_sum_copp = 0.0;
1498 let mut succeed = 0;
1499
1500 for i_exp in 0..n_exp {
1501 let n: usize = 1000;
1502 let dim = 7;
1503 let mut robot = Robot::with_capacity(dim, n);
1504
1505 let mut rng = rand::rng();
1506 let (s, derivs, omega, phi) =
1507 lissajous_path_for_test(dim, n, &mut rng).expect("random range is valid");
1508 robot.with_s(&s.as_view())?;
1509 robot.with_q(
1510 &derivs.q.as_view(),
1511 &derivs.dq.as_ref().unwrap().as_view(),
1512 &derivs.ddq.as_ref().unwrap().as_view(),
1513 derivs.dddq.as_ref().map(|m| m.as_view()).as_ref(),
1514 0,
1515 )?;
1516 add_symmetric_axial_limits_for_test(&mut robot, 1.0, 1.0, Some(5.0))?;
1517
1518 let topp2_problem = Topp2ProblemBuilder::new(&robot, (0, n - 1), (0.0, 0.0)).build()?;
1519 let start = Instant::now();
1521 let options_ra0 = ReachSet2OptionsBuilder::new()
1522 .lp_feas_tol(1E-9)
1523 .a_cmp_abs_tol(1E-9)
1524 .a_cmp_rel_tol(1E-9)
1525 .build()?;
1526 let a_ra0 = topp2_ra(&topp2_problem, &options_ra0)?;
1527 let tc_ra0 = start.elapsed().as_secs_f64() * 1E3;
1528 let (tf_ra0, _) = s_to_t_topp2(s.as_slice(), &a_ra0, 0.0);
1529
1530 let objective = [CoppObjective::Time(1.0)];
1531 let copp3_problem =
1532 Copp3ProblemBuilder::new(&mut robot, &objective, 0, &a_ra0, (0.0, 0.0), (0.0, 0.0))
1533 .build_with_linearization()?;
1534
1535 let start = Instant::now();
1537 let (a_copp, b_copp, num_stationary_copp) = {
1538 let mut settings = default_clarabel_settings();
1539 settings.tol_gap_rel = 1E-6;
1540 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1541 .allow_almost_solved(true)
1542 .build()?;
1543 let (result, solution) = match copp3_socp_expert(&copp3_problem, &options) {
1544 Ok(res) => res,
1545 Err(_) => {
1546 crate::verbosity_log!(
1547 crate::diag::Verbosity::Debug,
1548 "Exp #{}: Clarabel solver failed in copp3_socp_expert!",
1549 i_exp + 1
1550 );
1551 continue;
1552 }
1553 };
1554 if let Some(result) = result {
1555 result
1556 } else {
1557 return Err(CoppError::ClarabelSolverStatus(
1558 "copp3_socp".into(),
1559 solution.status,
1560 ));
1561 }
1562 };
1563 let tc_copp = start.elapsed().as_secs_f64() * 1E3;
1564 let (tf_copp, _) =
1566 s_to_t_topp3(s.as_slice(), &a_copp, &b_copp, num_stationary_copp, 0.0);
1567
1568 let start = Instant::now();
1570 let options_qp = ClarabelOptionsBuilder::new()
1571 .allow_almost_solved(true)
1572 .build()?;
1573 let (a_qp, b_qp, _) = topp3_socp(&copp3_problem.as_topp3_problem(), &options_qp)?;
1574 let tc_qp = start.elapsed().as_secs_f64() * 1E3;
1575 let (tf_qp, _) = s_to_t_topp3(s.as_slice(), &a_qp, &b_qp, num_stationary_copp, 0.0);
1577
1578 let (obj_qp, _) =
1579 objective_value_copp3_opt(&copp3_problem, &a_qp, &b_qp, num_stationary_copp);
1580 let (obj_copp, _) =
1581 objective_value_copp3_opt(&copp3_problem, &a_copp, &b_copp, num_stationary_copp);
1582
1583 if flag_print_step {
1584 crate::verbosity_log!(
1585 crate::diag::Verbosity::Summary,
1586 "Exp #{}: tc_ra = {:.4} ms, tc_qp = {:.4} ms, tc_copp = {:.4} ms, tf_ra = {:.6}, tf_qp = {:.6}, tf_copp = {:.6}, obj_qp = {:.6}, obj_copp = {:.6}",
1587 i_exp + 1,
1588 tc_ra0,
1589 tc_qp,
1590 tc_copp,
1591 tf_ra0,
1592 tf_qp,
1593 tf_copp,
1594 obj_qp,
1595 obj_copp
1596 );
1597 }
1598
1599 if (tf_copp - tf_qp).abs() > 1e-4 || (obj_copp - obj_qp).abs() > 1e-4 {
1600 crate::verbosity_log!(
1601 crate::diag::Verbosity::Summary,
1602 "omega = {omega:?}\nphi = {phi:?}"
1603 );
1604 crate::verbosity_log!(
1605 crate::diag::Verbosity::Debug,
1606 "COPP3 time optimality failed at Exp #{}! tf_copp - tf_qp = {}, obj_copp - obj_qp = {}",
1607 i_exp + 1,
1608 tf_copp - tf_qp,
1609 obj_copp - obj_qp
1610 );
1611 }
1612
1613 tc_sum_ra += tc_ra0;
1614 tc_sum_qp += tc_qp;
1615 tc_sum_copp += tc_copp;
1616 tf_sum_ra += tf_ra0;
1617 tf_sum_qp += tf_qp;
1618 tf_sum_copp += tf_copp;
1619 obj_sum_qp += obj_qp;
1620 obj_sum_copp += obj_copp;
1621 succeed += 1;
1622 }
1623
1624 crate::verbosity_log!(
1625 crate::diag::Verbosity::Summary,
1626 "Average {n_exp} experiments (fail {}): tc_ra = {:.4} ms, tc_qp = {:.4} ms, tc_copp = {:.4} ms, tf_ra = {:.6}, tf_qp = {:.6}, tf_copp = {:.6}, obj_qp = {:.6}, obj_copp = {:.6}",
1627 n_exp - succeed,
1628 tc_sum_ra / succeed as f64,
1629 tc_sum_qp / succeed as f64,
1630 tc_sum_copp / succeed as f64,
1631 tf_sum_ra / succeed as f64,
1632 tf_sum_qp / succeed as f64,
1633 tf_sum_copp / succeed as f64,
1634 obj_sum_qp / succeed as f64,
1635 obj_sum_copp / succeed as f64
1636 );
1637
1638 Ok(())
1639 }
1640
1641 fn run_test_all_objectives_repeated(
1642 n_exp: usize,
1643 flag_print_step: bool,
1644 ) -> Result<(), CoppError> {
1645 let mut tc_sum_case0 = 0.0;
1646 let mut tc_sum_case1 = 0.0;
1647 let mut tc_sum_case2 = 0.0;
1648 let mut tc_sum_case3 = 0.0;
1649 let mut tc_sum_case4 = 0.0;
1650 let mut obj_sum_case0 = vec![0.0; 4];
1651 let mut obj_sum_case1 = vec![0.0; 4];
1652 let mut obj_sum_case2 = vec![0.0; 4];
1653 let mut obj_sum_case3 = vec![0.0; 4];
1654 let mut obj_sum_case4 = vec![0.0; 4];
1655
1656 let mut succeed = 0;
1657
1658 for i_exp in 0..n_exp {
1659 let n: usize = 1000;
1660 let dim = 7;
1661 let mut robot = Robot::with_capacity(dim, n);
1662
1663 let mut rng = rand::rng();
1664 let (s, derivs, omega, phi) =
1665 lissajous_path_for_test(dim, n, &mut rng).expect("random range is valid");
1666 if flag_print_step {
1688 crate::verbosity_log!(
1689 crate::diag::Verbosity::Summary,
1690 "omega = {omega:?}\nphi = {phi:?}"
1691 );
1692 }
1693 robot.with_s(&s.as_view())?;
1694 robot.with_q(
1695 &derivs.q.as_view(),
1696 &derivs.dq.as_ref().unwrap().as_view(),
1697 &derivs.ddq.as_ref().unwrap().as_view(),
1698 derivs.dddq.as_ref().map(|m| m.as_view()).as_ref(),
1699 0,
1700 )?;
1701 add_symmetric_axial_limits_for_test(&mut robot, 1.0, 1.0, Some(5.0))?;
1702
1703 let topp2_problem = Topp2ProblemBuilder::new(&robot, (0, n - 1), (0.0, 0.0)).build()?;
1704 let options_ra0 = ReachSet2OptionsBuilder::new()
1706 .lp_feas_tol(1E-9)
1707 .a_cmp_abs_tol(1E-9)
1708 .a_cmp_rel_tol(1E-9)
1709 .build()?;
1710 let a_ra0 = topp2_ra(&topp2_problem, &options_ra0)?;
1711
1712 let objectives_test = [
1714 CoppObjective::Time(1.0),
1715 CoppObjective::ThermalEnergy(1.0, &vec![1.0; dim]),
1716 CoppObjective::TotalVariationTorque(1.0, &vec![1.0; dim]),
1717 CoppObjective::Linear(1.0, &vec![0.0; n], &vec![1.0; n - 1]),
1718 ];
1719
1720 let obj_case0_src = [CoppObjective::Time(1.0)];
1722 let start = Instant::now();
1723 let (a_case0, b_case0, num_stationary) = {
1724 let copp3_problem = Copp3ProblemBuilder::new(
1725 &mut robot,
1726 &obj_case0_src,
1727 0,
1728 &a_ra0,
1729 (0.0, 0.0),
1730 (0.0, 0.0),
1731 )
1732 .build_with_linearization()?;
1733 let mut settings = default_clarabel_settings();
1734 settings.tol_gap_rel = 1E-6;
1735 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1736 .allow_almost_solved(true)
1737 .build()?;
1738 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1739 if let Some(result) = result {
1740 result
1741 } else {
1742 crate::verbosity_log!(
1743 crate::diag::Verbosity::Debug,
1744 "{:?}",
1745 CoppError::ClarabelSolverStatus(
1746 "copp3_socp (case 0)".into(),
1747 solution.status,
1748 )
1749 );
1750 continue;
1751 }
1752 };
1753 let tc_copp3_case0 = start.elapsed().as_secs_f64() * 1E3;
1754 let (_, obj_case0) = {
1755 let copp3_problem = Copp3ProblemBuilder::new(
1756 &mut robot,
1757 &objectives_test,
1758 0,
1759 &a_ra0,
1760 (0.0, 0.0),
1761 (0.0, 0.0),
1762 )
1763 .build_with_linearization()?;
1764 objective_value_copp3_opt(&copp3_problem, &a_case0, &b_case0, num_stationary)
1765 };
1766
1767 let obj_case1 = [
1769 CoppObjective::Time(1.0),
1770 CoppObjective::ThermalEnergy(1.0, &vec![1.0; dim]),
1771 ];
1772 let start = Instant::now();
1773 let (a_case1, b_case1, num_stationary) = {
1774 let copp3_problem = Copp3ProblemBuilder::new(
1775 &mut robot,
1776 &obj_case1,
1777 0,
1778 &a_ra0,
1779 (0.0, 0.0),
1780 (0.0, 0.0),
1781 )
1782 .build_with_linearization()?;
1783 let mut settings = default_clarabel_settings();
1784 settings.tol_gap_rel = 1E-6;
1785 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1786 .allow_almost_solved(true)
1787 .build()?;
1788 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1789 if let Some(result) = result {
1790 result
1791 } else {
1792 crate::verbosity_log!(
1793 crate::diag::Verbosity::Debug,
1794 "{:?}",
1795 CoppError::ClarabelSolverStatus(
1796 "copp3_socp (case 1)".into(),
1797 solution.status,
1798 )
1799 );
1800 continue;
1801 }
1802 };
1803 let tc_copp3_case1 = start.elapsed().as_secs_f64() * 1E3;
1804 let (_, obj_case1) = {
1805 let copp3_problem = Copp3ProblemBuilder::new(
1806 &mut robot,
1807 &objectives_test,
1808 0,
1809 &a_ra0,
1810 (0.0, 0.0),
1811 (0.0, 0.0),
1812 )
1813 .build_with_linearization()?;
1814 objective_value_copp3_opt(&copp3_problem, &a_case1, &b_case1, num_stationary)
1815 };
1816 if obj_case1[0] < obj_case0[0] - 1E-3 || obj_case1[1] - 1E-3 > obj_case0[1] {
1817 let (tf_case0, _) = s_to_t_topp2(s.as_slice(), &a_case0, 0.0);
1818 let (tf_case1, _) = s_to_t_topp2(s.as_slice(), &a_case1, 0.0);
1819 crate::verbosity_log!(
1820 crate::diag::Verbosity::Summary,
1821 "omega = {omega:?}\nphi = {phi:?}"
1822 );
1823 crate::verbosity_log!(
1824 crate::diag::Verbosity::Summary,
1825 "Case 0: obj_time = {}, obj_thermal_energy = {}, tf = {}",
1826 obj_case0[0],
1827 obj_case0[1],
1828 tf_case0
1829 );
1830 crate::verbosity_log!(
1831 crate::diag::Verbosity::Summary,
1832 "Case 1: obj_time = {}, obj_thermal_energy = {}, tf = {}",
1833 obj_case1[0],
1834 obj_case1[1],
1835 tf_case1
1836 );
1837 crate::verbosity_log!(
1838 crate::diag::Verbosity::Summary,
1839 "Interesting... Cases 0 and 1"
1840 );
1841 }
1842
1843 let obj_case2 = [
1845 CoppObjective::Time(1.0),
1846 CoppObjective::ThermalEnergy(10.0, &vec![1.0; dim]),
1847 ];
1848 let start = Instant::now();
1849 let (a_case2, b_case2, num_stationary) = {
1850 let copp3_problem = Copp3ProblemBuilder::new(
1851 &mut robot,
1852 &obj_case2,
1853 0,
1854 &a_ra0,
1855 (0.0, 0.0),
1856 (0.0, 0.0),
1857 )
1858 .build_with_linearization()?;
1859 let mut settings = default_clarabel_settings();
1860 settings.tol_gap_rel = 1E-6;
1861 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1862 .allow_almost_solved(true)
1863 .build()?;
1864 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1865 if let Some(result) = result {
1866 result
1867 } else {
1868 crate::verbosity_log!(
1869 crate::diag::Verbosity::Debug,
1870 "{:?}",
1871 CoppError::ClarabelSolverStatus(
1872 "copp3_socp (case 2)".into(),
1873 solution.status,
1874 )
1875 );
1876 continue;
1877 }
1878 };
1879 let tc_copp3_case2 = start.elapsed().as_secs_f64() * 1E3;
1880 let (_, obj_case2) = {
1881 let copp3_problem = Copp3ProblemBuilder::new(
1882 &mut robot,
1883 &objectives_test,
1884 0,
1885 &a_ra0,
1886 (0.0, 0.0),
1887 (0.0, 0.0),
1888 )
1889 .build_with_linearization()?;
1890 objective_value_copp3_opt(&copp3_problem, &a_case2, &b_case2, num_stationary)
1891 };
1892 if obj_case2[0] < obj_case1[0] - 1E-3 || obj_case2[1] - 1E-3 > obj_case1[1] {
1893 crate::verbosity_log!(
1894 crate::diag::Verbosity::Summary,
1895 "omega = {omega:?}\nphi = {phi:?}"
1896 );
1897 crate::verbosity_log!(
1898 crate::diag::Verbosity::Summary,
1899 "Case 1: obj_time = {}, obj_thermal_energy = {}",
1900 obj_case1[0],
1901 obj_case1[1]
1902 );
1903 crate::verbosity_log!(
1904 crate::diag::Verbosity::Summary,
1905 "Case 2: obj_time = {}, obj_thermal_energy = {}",
1906 obj_case2[0],
1907 obj_case2[1]
1908 );
1909 crate::verbosity_log!(
1910 crate::diag::Verbosity::Summary,
1911 "Interesting... Cases 1 and 2"
1912 );
1913 }
1914
1915 let obj_case3 = [
1917 CoppObjective::Time(1.0),
1918 CoppObjective::TotalVariationTorque(1.0, &vec![1.0; dim]),
1919 ];
1920 let start = Instant::now();
1921 let (a_case3, b_case3, num_stationary) = {
1922 let copp3_problem = Copp3ProblemBuilder::new(
1923 &mut robot,
1924 &obj_case3,
1925 0,
1926 &a_ra0,
1927 (0.0, 0.0),
1928 (0.0, 0.0),
1929 )
1930 .build_with_linearization()?;
1931 let mut settings = default_clarabel_settings();
1932 settings.tol_gap_rel = 1E-6;
1933 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
1934 .allow_almost_solved(true)
1935 .build()?;
1936 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
1937 if let Some(result) = result {
1938 result
1939 } else {
1940 crate::verbosity_log!(
1941 crate::diag::Verbosity::Debug,
1942 "{:?}",
1943 CoppError::ClarabelSolverStatus(
1944 "copp3_socp (case 3)".into(),
1945 solution.status,
1946 )
1947 );
1948 continue;
1949 }
1950 };
1951 let tc_copp3_case3 = start.elapsed().as_secs_f64() * 1E3;
1952 let (_, obj_case3) = {
1953 let copp3_problem = Copp3ProblemBuilder::new(
1954 &mut robot,
1955 &objectives_test,
1956 0,
1957 &a_ra0,
1958 (0.0, 0.0),
1959 (0.0, 0.0),
1960 )
1961 .build_with_linearization()?;
1962 objective_value_copp3_opt(&copp3_problem, &a_case3, &b_case3, num_stationary)
1963 };
1964 if obj_case3[1] < obj_case1[1] - 1E-3 || obj_case3[2] - 1E-3 > obj_case1[2] {
1965 crate::verbosity_log!(
1966 crate::diag::Verbosity::Summary,
1967 "omega = {omega:?}\nphi = {phi:?}"
1968 );
1969 crate::verbosity_log!(
1970 crate::diag::Verbosity::Summary,
1971 "Case 1: obj_time = {}, obj_thermal_energy = {}, obj_total_variation_torque = {}",
1972 obj_case1[0],
1973 obj_case1[1],
1974 obj_case1[2]
1975 );
1976 crate::verbosity_log!(
1977 crate::diag::Verbosity::Summary,
1978 "Case 3: obj_time = {}, obj_thermal_energy = {}, obj_total_variation_torque = {}",
1979 obj_case3[0],
1980 obj_case3[1],
1981 obj_case3[2]
1982 );
1983 crate::verbosity_log!(
1984 crate::diag::Verbosity::Summary,
1985 "Interesting... Cases 1 and 3"
1986 );
1987 }
1988
1989 let obj_case4 = [
1991 CoppObjective::Time(1.0),
1992 CoppObjective::Linear(1.0, &vec![0.0; n], &vec![1.0; n]),
1993 ];
1994 let start = Instant::now();
1995 let (a_case4, b_case4, num_stationary) = {
1996 let copp3_problem = Copp3ProblemBuilder::new(
1997 &mut robot,
1998 &obj_case4,
1999 0,
2000 &a_ra0,
2001 (0.0, 0.0),
2002 (0.0, 0.0),
2003 )
2004 .build_with_linearization()?;
2005 let mut settings = default_clarabel_settings();
2006 settings.tol_gap_rel = 1E-6;
2007 let options = ClarabelOptionsBuilder::with_clarabel_setting(settings)
2008 .allow_almost_solved(true)
2009 .build()?;
2010 let (result, solution) = copp3_socp_expert(&copp3_problem, &options)?;
2011 if let Some(result) = result {
2012 result
2013 } else {
2014 crate::verbosity_log!(
2015 crate::diag::Verbosity::Debug,
2016 "{:?}",
2017 CoppError::ClarabelSolverStatus(
2018 "copp3_socp (case 4)".into(),
2019 solution.status,
2020 )
2021 );
2022 continue;
2023 }
2024 };
2025 let tc_copp3_case4 = start.elapsed().as_secs_f64() * 1E3;
2026 let (_, obj_case4) = {
2027 let copp3_problem = Copp3ProblemBuilder::new(
2028 &mut robot,
2029 &objectives_test,
2030 0,
2031 &a_ra0,
2032 (0.0, 0.0),
2033 (0.0, 0.0),
2034 )
2035 .build_with_linearization()?;
2036 objective_value_copp3_opt(&copp3_problem, &a_case4, &b_case4, num_stationary)
2037 };
2038 if obj_case4[1] < obj_case1[1] - 1E-3 || obj_case4[3] - 1E-3 > obj_case1[3] {
2039 crate::verbosity_log!(
2040 crate::diag::Verbosity::Summary,
2041 "omega = {omega:?}\nphi = {phi:?}"
2042 );
2043 crate::verbosity_log!(
2044 crate::diag::Verbosity::Summary,
2045 "Case 1: obj_time = {}, obj_thermal_energy = {}, obj_linear = {}",
2046 obj_case1[0],
2047 obj_case1[1],
2048 obj_case1[3]
2049 );
2050 crate::verbosity_log!(
2051 crate::diag::Verbosity::Summary,
2052 "Case 4: obj_time = {}, obj_thermal_energy = {}, obj_linear = {}",
2053 obj_case4[0],
2054 obj_case4[1],
2055 obj_case4[3]
2056 );
2057 crate::verbosity_log!(
2058 crate::diag::Verbosity::Summary,
2059 "Interesting... Cases 1 and 4"
2060 );
2061 }
2062 if obj_case4[2] < obj_case2[2] - 1E-3 || obj_case4[3] - 1E-3 > obj_case2[3] {
2063 crate::verbosity_log!(
2064 crate::diag::Verbosity::Summary,
2065 "omega = {omega:?}\nphi = {phi:?}"
2066 );
2067 crate::verbosity_log!(
2068 crate::diag::Verbosity::Summary,
2069 "Case 2: obj_time = {}, obj_total_variation_torque = {}, obj_linear = {}",
2070 obj_case2[0],
2071 obj_case2[2],
2072 obj_case2[3]
2073 );
2074 crate::verbosity_log!(
2075 crate::diag::Verbosity::Summary,
2076 "Case 4: obj_time = {}, obj_total_variation_torque = {}, obj_linear = {}",
2077 obj_case4[0],
2078 obj_case4[2],
2079 obj_case4[3]
2080 );
2081 crate::verbosity_log!(
2082 crate::diag::Verbosity::Summary,
2083 "Interesting... Cases 2 and 4"
2084 );
2085 }
2086
2087 succeed += 1;
2088
2089 if flag_print_step {
2090 crate::verbosity_log!(
2091 crate::diag::Verbosity::Summary,
2092 "Exp #{}:\n Case 0: tc={:.3}ms, obj={:?}\n Case 1: tc={:.3}ms, obj={:?}\n Case 2: tc={:.3}ms, obj={:?}\n Case 3: tc={:.3}ms, obj={:?}\n Case 4: tc={:.3}ms, obj={:?}",
2093 i_exp + 1,
2094 tc_copp3_case0,
2095 obj_case0,
2096 tc_copp3_case1,
2097 obj_case1,
2098 tc_copp3_case2,
2099 obj_case2,
2100 tc_copp3_case3,
2101 obj_case3,
2102 tc_copp3_case4,
2103 obj_case4
2104 );
2105 }
2106
2107 tc_sum_case0 += tc_copp3_case0;
2108 tc_sum_case1 += tc_copp3_case1;
2109 tc_sum_case2 += tc_copp3_case2;
2110 tc_sum_case3 += tc_copp3_case3;
2111 tc_sum_case4 += tc_copp3_case4;
2112 for i in 0..obj_case0.len() {
2113 obj_sum_case0[i] += obj_case0[i];
2114 obj_sum_case1[i] += obj_case1[i];
2115 obj_sum_case2[i] += obj_case2[i];
2116 obj_sum_case3[i] += obj_case3[i];
2117 obj_sum_case4[i] += obj_case4[i];
2118 }
2119 }
2120
2121 for i in 0..4 {
2122 obj_sum_case0[i] /= n_exp as f64;
2123 obj_sum_case1[i] /= n_exp as f64;
2124 obj_sum_case2[i] /= n_exp as f64;
2125 obj_sum_case3[i] /= n_exp as f64;
2126 obj_sum_case4[i] /= n_exp as f64;
2127 }
2128
2129 crate::verbosity_log!(
2130 crate::diag::Verbosity::Summary,
2131 "Average {n_exp} experiments (fail {}):\n Case 0: tc={:.3}ms, obj={obj_sum_case0:?}\n Case 1: tc={:.3}ms, obj={obj_sum_case1:?}\n Case 2: tc={:.3}ms, obj={obj_sum_case2:?}\n Case 3: tc={:.3}ms, obj={obj_sum_case3:?}\n Case 4: tc={:.3}ms, obj={obj_sum_case4:?}",
2132 n_exp - succeed,
2133 tc_sum_case0 / succeed as f64,
2134 tc_sum_case1 / succeed as f64,
2135 tc_sum_case2 / succeed as f64,
2136 tc_sum_case3 / succeed as f64,
2137 tc_sum_case4 / succeed as f64
2138 );
2139
2140 Ok(())
2141 }
2142}